package com.atguigu.gmall.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.common.util.IpUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.List;

/**
 * @Author atguigu-mqx
 * @Description TODO
 * @Date 2023/7/21 11:21
 * @Version 1.0
 */
@Component
public class AuthFilter implements GlobalFilter {

    /*
    以下业务必须要登录才能访问.
    authUrls:
        url: trade.html,myOrder.html,list.html
     */
    @Value("${authUrls.url}")
    private String authUrlsUrl;
    @Autowired
    private RedisTemplate redisTemplate;

    private AntPathMatcher antPathMatcher = new AntPathMatcher();



    /**
     * 过滤器
     * @param exchange  web请求
     * @param chain 过滤器链
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //  1.  过滤掉静态资源的拦截.
        //  获取请求,响应对象
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        //  request.getURI() = http://localhost:8206/api/product/inner/getSkuInfo/22
        //  获取请求路径
        String path = request.getURI().getPath();
        //  path = /api/product/inner/getSkuInfo/22
        //  判断是否匹配.
        if (antPathMatcher.match("/**/css/**",path) ||
                antPathMatcher.match("/**/js/**",path) ||
                antPathMatcher.match("/**/img/**",path)){
            //  放行
            chain.filter(exchange);
        }
        //  2.  判断用户是否访问了内部数据接口.
        //  http://localhost:8206/api/product/inner/getSkuInfo/22
        if (antPathMatcher.match("/**/inner/**",path)){
            //  不能访问，所以要返回
            return this.out(response, ResultCodeEnum.PERMISSION);
        }
        //  3.  限制用户在未登录情况下的访问 带有/auth/ 这样的控制器!
        //  获取用户Id
        String userId = this.getUserId(request);
        //  获取临时用户Id
        String userTempId = this.getUserTempId(request);
        //  判断条件
        if ("-1".equals(userId)){
            return this.out(response, ResultCodeEnum.ILLEGAL_REQUEST);
        }
        //  判断匹配
        if (antPathMatcher.match("/**/auth/**",path)){
            //  判断用户是否登录
            if (StringUtils.isEmpty(userId)){
                //  不能访问，所以要返回
                return this.out(response, ResultCodeEnum.LOGIN_AUTH);
            }
        }

        //  4.  判断用户访问哪些业务时，必须要登录.
        //  trade.html,myOrder.html,list.html path 中是否包含这些业务控制器。
        String[] split = authUrlsUrl.split(",");
        //  判断
        if (null != split && split.length>0){
            //  循环判断.
            for (String url : split) {
                //  判断是否包含以上信息.
                //  你在未登录的情况下访问了需要登录的业务模块
                if (path.indexOf(url)!=-1 && StringUtils.isEmpty(userId)){
                    //  需要拦截，并调整到登录页面。
                    response.setStatusCode(HttpStatus.SEE_OTHER);
                    //  设置重定向的路径.
                    response.getHeaders().set(HttpHeaders.LOCATION,"http://passport.gmall.com/login.html?originUrl="+request.getURI());
                    //  重定向
                    return response.setComplete();
                }
            }
        }
        //  比较重要的操作. 将用户信息userId统一放入请求头中
        if (!StringUtils.isEmpty(userId) || !StringUtils.isEmpty(userTempId)){

            //  判断用户Id
            if (!StringUtils.isEmpty(userId)){
                //  ServerHttpRequest request
                //  我们将userId 存储到request 中header中.
                request.mutate().header("userId",userId).build();
            }
            //  判断临时用户Id
            if (!StringUtils.isEmpty(userTempId)){
                //  ServerHttpRequest request
                //  我们将userId 存储到request 中header中.
                request.mutate().header("userTempId",userTempId).build();
            }
            //  终止执行. exchange--> 这个对象中里面没有设置过用户Id
            //  request-->封装到exchange中。
            return chain.filter(exchange.mutate().request(request).build());
        }
        //  默认走其他过滤器链
        return chain.filter(exchange);
    }

    /**
     * 获取临时用户Id
     * @param request
     * @return
     */
    private String getUserTempId(ServerHttpRequest request) {
        //  声明临时用户Id 变量.
        String userTempId = "";
        //  userTempId 存储在cookie中 ，也有可能存储在header中.
        HttpCookie httpCookie = request.getCookies().getFirst("userTempId");
        //  判断
        if (null != httpCookie){
            userTempId = httpCookie.getValue();
        } else {
            //  在cookie 中获取不到, 从请求头中获取数据.
            List<String> list = request.getHeaders().get("userTempId");
            if (!StringUtils.isEmpty(list)){
                userTempId = list.get(0);
            }
        }
        return userTempId;
    }

    /**
     * 获取用户Id
     * @param request
     * @return
     */
    private String getUserId(ServerHttpRequest request) {
        //  声明token 字符串
        String token = "";
        //  获取用户Id 必须从缓存中获取. 必须先获取到token，由token 组成缓存的key!
        //  token 存储在cookie中 ，也有可能存储在header中.
        HttpCookie httpCookie = request.getCookies().getFirst("token");
        //  判断
        if (null != httpCookie){
            token = httpCookie.getValue();
        }else {
            //  在cookie 中获取不到, 从请求头中获取数据.
            List<String> list = request.getHeaders().get("token");
            if (!StringUtils.isEmpty(list)){
                token = list.get(0);
            }
        }
        //  判断token 是否能够获取到
        if (!StringUtils.isEmpty(token)){
            //  组成缓存的key
            String loginKey = "user:login:"+token;
            String userJson = (String) this.redisTemplate.opsForValue().get(loginKey);
            if (!StringUtils.isEmpty(userJson)){
                //  将userJson 转换为对象
                JSONObject userInfo = JSONObject.parseObject(userJson);
                //  判断
                if (null!=userInfo){
                    //  获取数据
                    String ip = (String) userInfo.get("ip");
                    if (ip.equals(IpUtil.getGatwayIpAddress(request))){
                        //  正确返回用户Id
                        String userId = (String) userInfo.get("userId");
                        return userId;
                    }else {
                        //  非法盗用token
                        return "-1";
                    }
                }
            }
        }
        return "";
    }

    /**
     * 响应方法
     * @param response
     * @param resultCodeEnum
     * @return
     */
    private Mono<Void> out(ServerHttpResponse response, ResultCodeEnum resultCodeEnum) {
        //  提示信息;
        //  String message = resultCodeEnum.getMessage();
        Result<Object> result = Result.build(null, resultCodeEnum);
        DataBufferFactory bufferFactory = response.bufferFactory();
        //  DataBuffer wrap = bufferFactory.wrap(message.getBytes());
        DataBuffer wrap = bufferFactory.wrap(JSON.toJSONString(result).getBytes());
        //  设置当前请求头类型.  text/html
        response.getHeaders().add("content-Type","application/json;charset=utf-8");
        //  做响应
        return response.writeWith(Mono.just(wrap));
    }
}
